package com.ruoyi.onlyoffice.service.impl;


import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.ruoyi.common.config.RuoYiConfig;
import com.ruoyi.common.core.redis.RedisCache;
import com.ruoyi.common.utils.JWTUtil;
import com.ruoyi.common.utils.SecurityUtils;
import com.ruoyi.framework.web.service.TokenService;
import com.ruoyi.onlyoffice.config.OnlyProperties;
import com.ruoyi.onlyoffice.domain.FileConfig;
import com.ruoyi.onlyoffice.domain.FileContext;
import com.ruoyi.onlyoffice.domain.FileMetadata;
import com.ruoyi.onlyoffice.domain.OnFile;
import com.ruoyi.onlyoffice.handler.FileHandler;
import com.ruoyi.onlyoffice.handler.OnlyOfficeConfigFactory;
import com.ruoyi.onlyoffice.handler.SaveFileProcessor;
import com.ruoyi.onlyoffice.mapper.OnFileMapper;
import com.ruoyi.onlyoffice.service.IOnFileService;
import com.ruoyi.onlyoffice.utils.FileUtil;
import com.ruoyi.system.service.ISysUserService;
import org.apache.commons.io.FileUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.util.*;
import java.util.concurrent.TimeUnit;


/**
 * oo存档文件Service业务层处理
 * 
 * @author ruoyi
 * @date 2023-08-25
 */
@Service
public class OnFileServiceImpl implements IOnFileService
{
    @Autowired(required = false)
    private OnFileMapper onFileMapper;

    @Autowired(required = false)
    private FileHandler fileHandler;

    @Autowired(required = false)
    private OnlyOfficeConfigFactory onlyOfficeConfigFactory;

    @Autowired(required = false)
    private OnlyProperties onlyProperties;

    @Autowired
    private RedisCache redisCache;

    @Autowired
    private FileContext tempFileContext;

    @Autowired
    private SaveFileProcessor saveFileProcessor;

    @Autowired
    private ISysUserService sysUserService;

    @Autowired
    private TokenService tokenService;


    private String EDIT = "edit";
    private String VIEW = "view";

    /**
     * 查询oo存档文件
     * 
     * @param fileId oo存档文件ID
     * @return oo存档文件
     */
    @Override
    public OnFile selectOnFileById(String fileId)
    {
        return onFileMapper.selectOnFileById(fileId);
    }

    /**
     * 根据报告id查询最大版本号
     * @param reportId
     * @return
     */
    @Override
    public String selectMaxVersion(String reportId) {
        return onFileMapper.selectMaxVersion(reportId);
    }

    /**
     * 根据报告id获取最新记录
     * @param reportId
     * @return
     */
    @Override
    public OnFile selectNewVersion(String reportId) {
        return onFileMapper.selectNewVersion(reportId);
    }

    /**
     * 查询oo存档文件列表
     * 
     * @param onFile oo存档文件
     * @return oo存档文件
     */
    @Override
    public List<OnFile> selectOnFileList(OnFile onFile)
    {
        return onFileMapper.selectOnFileList(onFile);
    }

    /**
     * 根据报告id查询历史版本
     * @param reportId
     * @return
     */
    @Override
    public List<OnFile> selectOnFileByReportId(String reportId) {
        return onFileMapper.selectOnFileByReportId(reportId);
    }

    /**
     * 新增oo存档文件
     * 
     * @param onFile oo存档文件
     * @return 结果
     */
    @Override
    public int insertOnFile(OnFile onFile)
    {
        return onFileMapper.insertOnFile(onFile);
    }

    /**
     * 修改oo存档文件
     * 
     * @param onFile oo存档文件
     * @return 结果
     */
    @Override
    public int updateOnFile(OnFile onFile)
    {
        return onFileMapper.updateOnFile(onFile);
    }

    /**
     * 批量删除oo存档文件
     * 
     * @param fileIds 需要删除的oo存档文件ID
     * @return 结果
     */
    @Override
    public int deleteOnFileByIds(String[] fileIds)
    {
        return onFileMapper.deleteOnFileByIds(fileIds);
    }

    /**
     * 删除oo存档文件信息
     * 
     * @param fileId oo存档文件ID
     * @return 结果
     */
    @Override
    public int deleteOnFileById(String fileId)
    {
        return onFileMapper.deleteOnFileById(fileId);
    }

    @Override
    public String saveFile(byte[] bytes, String fileType) throws Exception {
        String fileId = IdUtil.simpleUUID();


        File file = new File(RuoYiConfig.getOoFile() +"\\"+fileId+"."+fileType);
        File parentFile = file.getParentFile();
        if (!parentFile.exists()){
            parentFile.mkdirs();
        }
        try (FileOutputStream out = new FileOutputStream(RuoYiConfig.getOoFile()+"\\"+fileId+"."+fileType)) {
            out.write(bytes);
            out.flush();
        }
        return fileId;
    }

    @Override
    public void removeFile(String id) {
        OnFile byId = onFileMapper.selectOnFileById(id);
        File file = new File(RuoYiConfig.getOoFile()+"\\"+id+"."+byId.getFileType());
        System.out.println("删除文件");
        FileUtils.deleteQuietly(file);
        onFileMapper.deleteOnFileById(id);
    }

    @Override
    public void download(String id, String isBrowser, HttpServletResponse response) {
        OnFile byId = onFileMapper.selectOnFileById(id);

        //判断是否有值
        if (StrUtil.isNotEmpty(isBrowser)){
            FileUtil.downloadAttachment(RuoYiConfig.getOoFile()+"\\"+id+"."+byId.getFileType(),byId.getFileName(),response);
        }else {
            FileUtil.downloadInline(RuoYiConfig.getOoFile()+"\\"+id+"."+byId.getFileType(),byId.getFileName(),response);
        }
    }

    @Override
    public Map openDocument(Map<String, Object> map, String mode, boolean collaborativeEditing, String userId) {
        //long fileSize = (long) map.get("fileSize");
        //
        //if (fileSize > 20*1024*1024) {
        //    // 添加操作日志
        //    throw new RuntimeException("文件超过【20MB】无法打开");
        //}

        if (EDIT.equals(mode)) {
            return documentEdit(map,collaborativeEditing, userId);
        }
        if (VIEW.equals(mode)) {
            return documentView(map, userId);
        }
        return null;
    }

    /**
     * 获取文件key
     * @param id
     * @return
     */
    @Override
    public String getKey(String id) {
        //判断缓存是否存在
        Object o = redisCache.getCacheObject(SecurityUtils.getLoginUser().getUser().getUserId() + "_" + id);
        if(o != null) {
            return (String) redisCache.getCacheObject(SecurityUtils.getLoginUser().getUser().getUserId() + "_" + id);
        }
        return (String) redisCache.getCacheObject("collaborativeEditing_" + id);
    }

    /**
     * 文件保存
     * @param key
     * @param userId
     * @return
     */
    @Override
    public String save(String key, String userId) {
        Map<String, Object> map = new HashMap<>();
        map.put("c", "forcesave");
        map.put("key", key);
        map.put("userdata", userId);
        if (null != onlyProperties.getSecret()){
            String token =  JWTUtil.createToken(map, onlyProperties.getSecret());
            //LoginUser loginUser = new LoginUser();
            //loginUser.setUser(sysUserService.selectUserById(Long.valueOf(userId)));
            //String token = tokenService.createToken(loginUser);
            map.put("token", token);
        }

        String bodyString = JSON.toJSONString(map);
        byte[] bodyByte = bodyString.getBytes(StandardCharsets.UTF_8);
        try {
            URL url = new URL(onlyProperties.getDocService() + onlyProperties.SAVE);
            HttpURLConnection connection = (HttpURLConnection) url.openConnection();
            connection.setRequestMethod("POST");
            connection.setDoOutput(true);
            connection.setRequestProperty("Content-Type", "application/json; charset=UTF-8");
            connection.setFixedLengthStreamingMode(bodyByte.length);
            connection.setRequestProperty("Accept", "application/json");
            connection.setConnectTimeout(onlyProperties.getTimeout());

            connection.connect();

            OutputStream os = connection.getOutputStream();
            os.write(bodyByte);

            InputStream stream = connection.getInputStream();

            if (stream == null) {
                throw new Exception("Could not get an answer");
            }

            /**将流转为字符串*/
            String jsonString = FileUtil.ConvertStreamToString(stream);
            connection.disconnect();
            JSONObject jsonObj = JSONObject.parseObject(jsonString);
            Object error = jsonObj.get("error");
            String msg = "";
            if (error != null) {
                switch ((Integer) error) {
                    case 1:
                        msg = "缺少文档密钥或找不到具有此类密钥的文档";
                        break;
                    case 2:
                        msg = "回调网址不正确";
                        break;
                    case 3:
                        msg = "内部服务器错误";
                        break;
                    case 4:
                        msg = "在收到强制保存命令之前，未对文档应用任何更改";
                        break;
                    case 5:
                        msg = "命令不正确";
                        break;
                    case 6:
                        msg = "令牌无效";
                        break;
                    default:
                        msg = "保存成功";
                }
            }
            return msg;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return "";
    }

    @Override
    public String getFileId(String key, String userId) {
        return (String)redisCache.getCacheObject(userId + key);
    }

    @Override
    public void close(JSONObject jsonObject) {
        int i = iskey(jsonObject.getString("key"), null);
        if (i <= 0) {
            removeTempFile(jsonObject);
            String id = (String) redisCache.getCacheObject("getID_" + jsonObject.getString("key"));
            redisCache.deleteObject("getID_" + id);
            redisCache.deleteObject("collaborativeEditing_" + id);
            redisCache.deleteObject(id);
        }
    }

    public void removeTempFile(JSONObject jsonObject) {
        String key = (String) jsonObject.get("key");
        FileHandler tempFileHandler = tempFileContext.getHandlerByKey(key);
        if (tempFileHandler != null) {
            tempFileHandler.removeTempFile(key);
        }
    }

    @Override
    public int iskey(String key, Integer users) {
        int i = 0;
        if (users != null) {
            redisCache.setCacheObject(key, users, 60 * 60 * 6, TimeUnit.SECONDS);
            i = users;
        } else {
            i = ((int)redisCache.getCacheObject(key)) - 1;
            redisCache.setCacheObject(key, i, 60 * 60 * 6, TimeUnit.SECONDS);
        }
        return i;
    }

    @Override
    public int getUserNum(String key) {
        return  (int)(redisCache.getCacheObject(key));
    }

    @Override
    public Integer getHistNum() {
        return onlyProperties.getHistNum();
    }

    @Override
    public void handlerStatus(JSONObject jsonObject) throws Exception {
        int status = jsonObject.getIntValue("status");
        String key = (String) jsonObject.get("key");
        //String reportId = (String) jsonObject.get("reportId");
        FileHandler tempFileHandler = tempFileContext.getHandlerByKey(key);
        if (Objects.nonNull(tempFileHandler)) {
            String url = jsonObject.getString("url");
            String changesurl = jsonObject.getString("changesurl");
            // 下载修改后文件
            byte[] fileByte = FileUtil.getFileByte(url);
            // 下载前一个文件和修改后文件的区别
            byte[] changes = FileUtil.getFileByte(changesurl);

            try {
                String fileExtension = FileUtil.getFileExtension(url);

                Optional<FileMetadata> tempFile = tempFileHandler.getTempFile(key);

                if (!tempFile.isPresent()){
                    throw new RuntimeException("文件元信息不存在");
                }
                saveFileProcessor.saveBeforeInitialization(tempFile.get().getFileInfo(),fileByte,fileExtension);
                // 保存文件
                saveFileProcessor.save(tempFile.get().getFileInfo(),fileByte,changes, key);
                saveFileProcessor.saveAfterInitialization(tempFile.get().getFileInfo(),fileByte,fileExtension);
            } catch (Exception e) {
                throw e;
            }
        }
    }

    /**
     * 更新报告中的reportId
     *
     * @param onFile
     */
    @Override
    public int updateFileIdByReportId(OnFile onFile) {
        return onFileMapper.updateFileIdByReportId(onFile);
    }


    private Map documentEdit(Map<String, Object> map,boolean collaborativeEditing, String userId)  {
        FileConfig fileConfigDTO = openEditConfig(map, "edit", collaborativeEditing, userId);
        String json = JSON.toJSONString(fileConfigDTO);

        Map<String, Object> config = JSON.parseObject(json, Map.class);


        return config;
    }


    private Map documentView(Map<String, Object> map, String userId)  {
        FileConfig fileConfigDTO = openEditConfig(map, "view", false, userId);
        String json = JSON.toJSONString(fileConfigDTO);
        Map<String, Object> config = JSON.parseObject(json, Map.class);

        return config;
    }

    private FileConfig openEditConfig(Map<String, Object> map, String mode, boolean collaborativeEditing, String userId) {
        try {
            map.put("mode", mode);
            //在编辑模式 生成临时文件，保存原文件信息
            FileMetadata tempFileInfo = fileHandler.handlerFile(map,collaborativeEditing) ;
            //生成配置文件 TODO: 控制文件权限
            //开始生成编辑器配置信息
            FileConfig fileConfigDTO = onlyOfficeConfigFactory.buildInitConfig(tempFileInfo.getUrl(), mode, tempFileInfo.getKey(), tempFileInfo.getOldName(), userId);
            //生成编辑器配置信息结束
            return fileConfigDTO;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }
}
